package com.db.williamchart.view;

import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;

import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import com.db.williamchart.ChartContract;
import com.db.williamchart.R;
import com.db.williamchart.animation.NoAnimation;
import com.db.williamchart.data.DataPoint;
import com.db.williamchart.data.Frame;
import com.db.williamchart.data.Label;
import com.db.williamchart.data.Paddings;
import com.db.williamchart.data.configuration.ChartConfiguration;
import com.db.williamchart.data.configuration.LineChartConfiguration;
import com.db.williamchart.extensions.ExtensionUtils;
import com.db.williamchart.extensions.ListUtils;
import com.db.williamchart.renderer.LineChartRenderer;

import java.util.Arrays;
import java.util.List;


public class LineChartView extends AxisChartView implements ChartContract.LineView {
    private final float defaultSmoothFactor = 0.20f;
    private final boolean defaultSmooth = false;
    private final float defaultLineThickness = 4F;
    private final int defaultFillColor = 0;
    private final int defaultLineColor = Color.BLACK;
    private final int defaultClickableArea = 24; // dp

    private boolean smooth = defaultSmooth;

    private float lineThickness = defaultLineThickness;

    private int fillColor = defaultFillColor;

    private int lineColor = defaultLineColor;

    private List<Integer> gradientFillColors = Arrays.asList(0, 0);

    @DrawableRes
    private int pointsDrawableRes = -1;

    public LineChartView(@NonNull Context context) {
        this(context,null);
    }

    public LineChartView(@NonNull Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public LineChartView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context, attrs);
    }

    @SuppressLint("CustomViewStyleable")
    private void init(@NonNull Context context, @Nullable AttributeSet attrs) {
        renderer = new LineChartRenderer(this, painter, new NoAnimation());
        handleAttributes(context.obtainStyledAttributes(attrs, R.styleable.LineChartAttrs));
        handleEditMode();
    }

    @Override
    public void drawLabels(List<Label> xLabels) {
        painter.prepare(labelsSize, labelsColor, labelsFont);
        labels.draw(canvas, painter.getPaint(), xLabels);
    }

    @Override
    public void drawGrid(Frame innerFrame, List<Float> xLabelsPositions, List<Float> yLabelsPositions) {
        grid.draw(canvas, innerFrame, xLabelsPositions, yLabelsPositions);
    }

    @Override
    public void drawDebugFrame(List<Frame> frames) {
        painter.prepare(-0x1000000, Paint.Style.STROKE);
        for (Frame frame : frames){
            canvas.drawRect(frame.toRect(), painter.getPaint());
        }
    }

    @Override
    public void drawLine(List<DataPoint> points) {
        Path linePath = !smooth ? ListUtils.toLinePath(points) : ListUtils.toSmoothLinePath(points, defaultSmoothFactor);
        painter.prepare(lineColor, Paint.Style.STROKE, lineThickness);
        canvas.drawPath(linePath, painter.getPaint());
    }

    @Override
    public void drawLineBackground(Frame innerFrame, List<DataPoint> points) {
        Path linePath = !smooth ? ListUtils.toLinePath(points) : ListUtils.toSmoothLinePath(points, defaultSmoothFactor);
        Path backgroundPath = createBackgroundPath(linePath, points, innerFrame.getBottom());

        if (fillColor != 0)
            painter.prepare(fillColor, Paint.Style.FILL);
        else
            painter.prepare(
                    innerFrame.toLinearGradient(gradientFillColors.get(0), gradientFillColors.get(1)),
                    Paint.Style.FILL
            );

        canvas.drawPath(backgroundPath, painter.getPaint());
    }

    private Path createBackgroundPath(Path path, List<DataPoint> points, float innerFrameBottom) {

        Path res = new Path(path);

        res.lineTo(points.get(points.size() - 1).getScreenPositionX(), innerFrameBottom);
        res.lineTo(points.get(0).getScreenPositionX(), innerFrameBottom);
        res.close();

        return res;
    }

    @Override
    public void drawPoints(List<DataPoint> points) {
        if (pointsDrawableRes != -1) {
            for (DataPoint dataPoint : points) {
                Drawable drawable = ExtensionUtils.getDrawable(this, pointsDrawableRes);
                if (drawable != null) {
                    ExtensionUtils.centerAt(drawable, dataPoint.getScreenPositionX(), dataPoint.getScreenPositionY());
                    drawable.draw(canvas);
                }
            }
        }
    }

    private void handleAttributes(TypedArray typedArray) {
        lineColor = typedArray.getColor(R.styleable.LineChartAttrs_chart_lineColor, lineColor);
        lineThickness =
                typedArray.getDimension(R.styleable.LineChartAttrs_chart_lineThickness, lineThickness);
        smooth = typedArray.getBoolean(R.styleable.LineChartAttrs_chart_smoothLine, smooth);
        pointsDrawableRes =
                typedArray.getResourceId(R.styleable.LineChartAttrs_chart_pointsDrawable, pointsDrawableRes);
        typedArray.recycle();
    }

    @Override
    ChartConfiguration getChartConfiguration() {
        int pointsDrawableWidth = -1;
        int pointsDrawableHeight = -1;
        if (pointsDrawableRes != -1) {
            Drawable drawable = ExtensionUtils.getDrawable(this, pointsDrawableRes);
            if (drawable != null) {
                pointsDrawableWidth = drawable.getIntrinsicWidth();
                pointsDrawableHeight = drawable.getIntrinsicHeight();
            }
        }
        return new LineChartConfiguration(
                getMeasuredWidth(),
                getMeasuredHeight(),
                new Paddings(
                        getPaddingLeft(),
                        getPaddingTop(),
                        getPaddingRight(),
                        getPaddingBottom()
                ),
                axis,
                labelsSize,
                scale,
                labelsFormatter,
                lineThickness,
                pointsDrawableWidth,
                pointsDrawableHeight,
                fillColor,
                gradientFillColors,
                ExtensionUtils.toPx(defaultClickableArea)
        );
    }

    public List<Integer> getGradientFillColors() {
        return gradientFillColors;
    }

    public void setGradientFillColors(List<Integer> gradientFillColors) {
        this.gradientFillColors = gradientFillColors;
    }

}
